import os
from enum import Enum

from util.log_s import LogS


def public_key_decrypt(rsa_public_key_der_b64, qr_code_cipher_b64):
    """
    RSA公钥解密
    注: 一般来说 RSA只能公钥加密，私钥解密
    :param rsa_public_key_der_b64:
    :param qr_code_cipher_b64:
    :return:
    """
    import base64
    from rsa import core, PublicKey, transform

    qr_code_cipher = base64.b64decode(qr_code_cipher_b64)
    public_key = base64.b64decode(rsa_public_key_der_b64)

    try:
        rsa_public_key = PublicKey.load_pkcs1_openssl_der(public_key)
        cipher_text_bytes = transform.bytes2int(qr_code_cipher)
        decrypted_text = core.decrypt_int(cipher_text_bytes, rsa_public_key.e, rsa_public_key.n)
        final_text = transform.int2bytes(decrypted_text)
        final_qr_code = final_text[final_text.index(0) + 1:]
        return final_qr_code.decode()
    except Exception as ex:
        LogS.warning(f'解密异常', ex)
        return None


class SimType(Enum):
    """
    仿真类型
    """
    # 本地环境
    LOCAL = 'LOCAL'
    # 全部
    ALL = 'ALL'
    # 预测
    YC = 'YC'
    # 排程
    PC = 'PC'
    # 排模
    PM = 'PM'
    # 时间窗
    TW = 'TW'


class SfDb(object):
    """
    SF数据量配置
    """
    __INSTANCE = None

    KEY_PREFIX = "DB"

    KEY_ENV = 'ENV'

    KEY_DECRYPT = 'DECRYPT_KEY'

    KEYS: list[str] = []

    DB_HOST: str = None
    DB_PORT: int = None
    DB_USER: str = None
    DB_PASSWORD: str = None
    DB_DEFAULT_DB: str = None
    DB_WAIT_TIMEOUT: int = None

    def __new__(cls, *args, **kwargs):
        if not cls.__INSTANCE:
            cls.__INSTANCE = super().__new__(cls, *args, **kwargs)
        return cls.__INSTANCE

    def __init__(self):
        self.KEYS = [_ for _ in self.__class__.__dict__ if _.startswith(self.KEY_PREFIX)]
        #
        if self.KEY_ENV in os.environ and os.environ[self.KEY_ENV] != SimType.LOCAL.value:
            self.check_env()
            LogS.debug(f'[{os.environ.get(self.KEY_ENV)}] {self}')
            return
        # ENV 为 `LOCAL`时，使用本地配置
        from local_db import LOCAL_SF
        for k in self.KEYS:
            setattr(self, k, getattr(LOCAL_SF, k))
        LogS.debug(f'[LOCAL] {self}')

    def check_env(self):
        """
        校验 & 读取 环境变量
        :return:
        """
        # 校验下，密码是否需要解密
        decrypt_key = os.environ.get(self.KEY_DECRYPT)
        none_keys: list[str] = []
        for k in self.KEYS:
            v = os.environ.get(k)
            if not v:
                none_keys.append(k)
            # 如果出现 加密key 且 当前为密码，则需要解密
            if 'PASSWORD' in k and decrypt_key:
                LogS.debug(f'[{os.environ.get(self.KEY_ENV)}] 解密方式，获取密码')
                v = public_key_decrypt(decrypt_key, v)
            setattr(self, k, v if k not in ('DB_PORT', 'DB_WAIT_TIMEOUT') else int(v))
        if none_keys:
            raise Exception(f'参数缺失 keys: {", ".join(none_keys)}')

    def __str__(self):
        properties = ',\n'.join(
            [_ + ' = ' + str(getattr(self, _) if 'PASSWORD' not in _ else '***') for _ in self.KEYS])
        return f'{self.__class__.__name__} (\n{properties}\n)'


class EnvConfig(object):
    """
    环境配置
    """
    gpu: bool = False

    def __init__(self):
        env_gpu = os.environ.get('ENV_GPU')
        if env_gpu:
            try:
                self.gpu = bool(int(env_gpu))
            except BaseException as ex:
                pass
        LogS.info(f'[YC] ENV: GPU环境 --> {self.gpu}')


class RunEnv(object):
    env: SimType = None

    def __init__(self):
        try:
            self.env = SimType(os.environ.get('ENV'))
        except BaseException as ex:
            LogS.warning(f'RunEnv 环境获取异常, 默认设置为：LOCAL')
            self.env = SimType.LOCAL

    def is_dev(self) -> bool:
        return self.env == SimType.LOCAL

    def is_test(self) -> bool:
        return self.env == SimType.ALL

    def is_prod(self) -> bool:
        return self.env in (SimType.PC, SimType.PM, SimType.TW, SimType.YC)


SF = SfDb()

ENV = EnvConfig()

RUN_ENV = RunEnv()

__all__ = ['SF', 'ENV', 'SimType', 'RUN_ENV', 'public_key_decrypt']

if __name__ == '__main__':
    print(SF)
